package co.gov.medellin.ssti.sub.model.dao.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;

import co.gov.medellin.ssti.sub.common.util.FiltrosReportesUtil;
import co.gov.medellin.ssti.sub.model.dao.ReporteTotalBeneficiariosDAO;
import co.gov.medellin.ssti.sub.model.vo.CondicionConsultaVO;
import co.gov.medellin.ssti.sub.model.vo.RepDetBeneficiarioVO;
import co.gov.medellin.ssti.sub.model.vo.ReporteTotalBeneficiosOtorVO;

public class ReporteTotalBeneficiariosDAOImpl2 extends
		NamedParameterJdbcDaoSupport implements ReporteTotalBeneficiariosDAO {

	public Collection<ReporteTotalBeneficiosOtorVO> findBeneficiarios(
			RepDetBeneficiarioVO criteria,
			Collection<CondicionConsultaVO> filtros) {
		Map<String, Object> namedParameters = new HashMap<String, Object>();

		// parte adicional de la consulta (criterios de busqueda.)
		StringBuffer queryVars = new StringBuffer();
		StringBuffer sbQueryVars = new StringBuffer();

		if ((Object) criteria.getCodeSede() != null) {
			queryVars.append(" AND sede.CODIGO = :sede.CODIGO ");
			sbQueryVars.append(" AND sede_.CODIGO = :sede.CODIGO ");
			namedParameters.put("sede.CODIGO", criteria.getCodeSede());
		}
		if ((Object) criteria.getCodeSubprograma() != null) {
			queryVars.append(" AND subpro.CODIGO = :subpro.CODIGO ");
			sbQueryVars.append(" AND subpro_.CODIGO = :subpro.CODIGO ");
			namedParameters.put("subpro.CODIGO", criteria.getCodeSubprograma());
		}
		if ((Object) criteria.getCodePrograma() != null) {
			queryVars.append(" AND prog.CODIGO = :prog.CODIGO ");
			sbQueryVars.append(" AND prog_.CODIGO = :prog.CODIGO ");
			namedParameters.put("prog.CODIGO", criteria.getCodePrograma());
		}
		if ((Object) criteria.getCodeArea() != null) {
			queryVars.append(" AND area.CODIGO = :area.CODIGO ");
			sbQueryVars.append(" AND area_.CODIGO = :area.CODIGO ");
			namedParameters.put("area.CODIGO", criteria.getCodeArea());
		}
		if ((Object) criteria.getDesde() != null
				&& (Object) criteria.getHasta() != null) {
			queryVars
					.append(" AND beneotor.FECHA_BENEFICIO BETWEEN TO_DATE(:DESDE,'dd/mm/rrrr') AND TO_DATE(:HASTA,'dd/mm/rrrr')");
			sbQueryVars
					.append(" AND beneotor_.FECHA_BENEFICIO BETWEEN TO_DATE(:DESDE,'dd/mm/rrrr') AND TO_DATE(:HASTA,'dd/mm/rrrr')");
			namedParameters.put("DESDE", criteria.getDesde());
			namedParameters.put("HASTA", criteria.getHasta());
		}
		if ((Object) criteria.getDesde() != null
				&& (Object) criteria.getHasta() == null) {
			queryVars
					.append(" AND beneotor.FECHA_BENEFICIO >= TO_DATE(:DESDE,'dd/mm/rrrr')");
			sbQueryVars
					.append(" AND beneotor_.FECHA_BENEFICIO >= TO_DATE(:DESDE,'dd/mm/rrrr')");
			namedParameters.put("DESDE", criteria.getDesde());
		}
		if ((Object) criteria.getDesde() == null
				&& (Object) criteria.getHasta() != null) {
			queryVars
					.append(" AND beneotor.FECHA_BENEFICIO <= TO_DATE(:HASTA,'dd/mm/rrrr')");
			sbQueryVars
					.append(" AND beneotor_.FECHA_BENEFICIO <= TO_DATE(:HASTA,'dd/mm/rrrr')");
			namedParameters.put("HASTA", criteria.getHasta());
		}

		Object[] ret = buildQueryFiltros(filtros);
		String queryFiltVars = (String) ret[0];
		Map<String, Object> queryFiltParams = (Map<String, Object>) ret[1];
		namedParameters.putAll(queryFiltParams);
		String sbQueryFiltVars = (String) ret[2];

		StringBuffer query = new StringBuffer();
		query.append("SELECT COUNT(DISTINCT beneotor.BEN_CODIGO) contador, "
				+ "sede.NOMBRE nomsede, prog.NOMBRE nompro, subpro.NOMBRE nomsubpro, area.NOMBRE secre "

				// subQuery
				+ ", (SELECT COUNT(DISTINCT beneficiario_.codigo) FROM TCS_BEN_PROGRAMA prog_, TCS_BEN_SUBPROGRAMA subpro_, TCS_BEN_BENEFICIO_OTORGADO beneotor_, TCS_BEN_SEDE sede_, TCS_BEN_BENEFICIARIO beneficiario_, TCS_BEN_AREA area_ WHERE beneotor_.SBPR_CODIGO =  subpro_.CODIGO AND area_.codigo = prog_.area_codigo AND beneotor_.SEDE_CODIGO = sede_.CODIGO AND subpro_.PRG_CODIGO = prog_.CODIGO AND beneficiario_.CODIGO = beneotor_.BEN_CODIGO AND prog_.vigencia='A' AND subpro_.vigencia='A'  "
				+ sbQueryVars.toString()
				+ sbQueryFiltVars
				+ " ) AS TOTAL_BENEFICIARIOS "

				+ "FROM TCS_BEN_PROGRAMA prog, TCS_BEN_SUBPROGRAMA subpro, TCS_BEN_BENEFICIO_OTORGADO beneotor, "
				+ "TCS_BEN_SEDE sede, TCS_BEN_BENEFICIARIO beneficiario, TCS_BEN_AREA area "
				+ "WHERE beneotor.SBPR_CODIGO =  subpro.CODIGO "
				+ "AND area.codigo = prog.area_codigo "
				+ "AND beneotor.SEDE_CODIGO = sede.CODIGO "
				+ "AND subpro.PRG_CODIGO = prog.CODIGO "
				+ "AND beneficiario.CODIGO = beneotor.BEN_CODIGO and prog.vigencia='A' AND subpro.vigencia='A'  ");

		query.append(queryVars.toString());
		query.append(queryFiltVars);
		query.append(" GROUP BY area.NOMBRE, prog.NOMBRE, subpro.NOMBRE, sede.NOMBRE ");
		query.append(" ORDER BY area.NOMBRE, prog.NOMBRE, subpro.NOMBRE, sede.NOMBRE ");

		List<ReporteTotalBeneficiosOtorVO> lbeneotor = new ArrayList<ReporteTotalBeneficiosOtorVO>();

		System.out.println("query: " + query.toString());
		for (String str : namedParameters.keySet()) {
			System.out.println(str + ":" + namedParameters.get(str));
		}

		long totalBeneficiarios = 0;
		int firstRow = 0;

		List<Map<String, Object>> rows = getNamedParameterJdbcTemplate()
				.queryForList(query.toString(), namedParameters);
		for (Map<String, Object> row : rows) {
			ReporteTotalBeneficiosOtorVO vo = new ReporteTotalBeneficiosOtorVO();
			vo.setNombrePrograma((String) row.get("nompro"));
			vo.setNombreSubprograma((String) row.get("nomsubpro"));
			vo.setNombreSede((String) row.get("nomsede"));
			BigDecimal contador = (BigDecimal) row.get("contador");
			if (contador != null) {
				vo.setContador(contador.longValueExact());
			}
			vo.setNombreArea((String) row.get("secre"));

			// total += rst.getLong("contador");
			if (firstRow == 0) {
				BigDecimal tot = (BigDecimal) row.get("TOTAL_BENEFICIARIOS");
				if (tot != null) {
					totalBeneficiarios = tot.longValueExact();
				}
				firstRow++;
			}
			lbeneotor.add(vo);
		}

		if (lbeneotor.size() > 0) {
			ReporteTotalBeneficiosOtorVO vo = new ReporteTotalBeneficiosOtorVO();
			vo.setNombrePrograma("VALOR TOTAL");
			// String valortotal = String.valueOf(total);
			// objbeneficios.setContador(total);
			vo.setContador(totalBeneficiarios);
			lbeneotor.add(vo);
		}
		return lbeneotor;
	}

	public RepDetBeneficiarioVO findFilters(
			RepDetBeneficiarioVO repDetBeneficiarioVO) {
		Map<String, Object> namedParameters = new HashMap<String, Object>();

		String strQuery = null;
		if (repDetBeneficiarioVO.getCodeSede() != null) {
			strQuery = "SELECT NOMBRE FROM TCS_BEN_SEDE WHERE CODIGO = :CODIGO ";

			namedParameters.put("CODIGO", repDetBeneficiarioVO.getCodeSede());
			try {
				String nombre = getNamedParameterJdbcTemplate().queryForObject(
						strQuery, namedParameters, String.class);
				repDetBeneficiarioVO.setNombreSede(nombre);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: handle exception
			}
		}
		if (repDetBeneficiarioVO.getCodePrograma() != null) {
			strQuery = "SELECT NOMBRE FROM TCS_BEN_PROGRAMA WHERE CODIGO = :CODIGO ";

			namedParameters.clear();
			namedParameters.put("CODIGO",
					repDetBeneficiarioVO.getCodePrograma());
			try {
				String nombre = getNamedParameterJdbcTemplate().queryForObject(
						strQuery, namedParameters, String.class);
				repDetBeneficiarioVO.setNombrePrograma(nombre);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: handle exception
			}
		}
		if (repDetBeneficiarioVO.getCodeSubprograma() != null) {
			strQuery = "SELECT NOMBRE FROM TCS_BEN_SUBPROGRAMA WHERE CODIGO = :CODIGO ";

			namedParameters.clear();
			namedParameters.put("CODIGO",
					repDetBeneficiarioVO.getCodeSubprograma());
			try {
				String nombre = getNamedParameterJdbcTemplate().queryForObject(
						strQuery, namedParameters, String.class);
				repDetBeneficiarioVO.setNombreSubprograma(nombre);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: handle exception
			}

		}
		if (repDetBeneficiarioVO.getCodeArea() != null) {
			strQuery = "SELECT NOMBRE FROM TCS_BEN_AREA WHERE CODIGO = :CODIGO ";

			namedParameters.clear();
			namedParameters.put("CODIGO", repDetBeneficiarioVO.getCodeArea());
			try {
				String nombre = getNamedParameterJdbcTemplate().queryForObject(
						strQuery, namedParameters, String.class);
				repDetBeneficiarioVO.setNombreArea(nombre);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: handle exception
			}
		}
		return repDetBeneficiarioVO;
	}

	/**
	 * HACK.. Metodo que retorna un Object[] que contiene los strings y
	 * parametros a concatenar en la consulta del metodo
	 * findBeneficiosBeneficiariosComuna.
	 * 
	 * @param filtros
	 * @return Array de Object, primer posicion String a concatenar (ej:
	 *         "AND ben.FECHA_NACIMIENTO >= TO_DATE('01/01/2000')", segunda
	 *         posicion List parametros query, tercera posicion string a
	 *         concatenar subquery, cuarta posicion List parametros subquery.
	 */
	private Object[] buildQueryFiltros(Collection<CondicionConsultaVO> filtros) {
		Map<String, Object> namedParameters = new HashMap<String, Object>();

		List<CondicionConsultaVO> lFiltros = (List<CondicionConsultaVO>) filtros;
		CondicionConsultaVO filtro = null;
		Object[] ret = new Object[4];
		StringBuffer query = new StringBuffer();
		StringBuffer sbQuery = new StringBuffer();

		if ((Object) filtros != null) {
			for (int i = 0; i < lFiltros.size(); i++) {
				filtro = (CondicionConsultaVO) lFiltros.get(i);
				String operador = filtro.getCodFiltro();
				String tipoDato = filtro.getTipoCampo();

				query.append(" AND ");
				sbQuery.append(" AND ");
				if (tipoDato.equals(FiltrosReportesUtil.TIPO_NUMERICO)) {

					if (filtro.getCodcampo().equals(FiltrosReportesUtil.EDAD)) {
						String fechaIni = "";
						String fechaFin = "";
						int edad1 = Integer.parseInt(filtro.getValor());
						fechaIni = FiltrosReportesUtil
								.convertAgeToDateOfBirth(edad1);
						if (operador.equals(FiltrosReportesUtil.ENTRE)) {
							int edad2 = Integer.parseInt(filtro.getValor2());
							fechaFin = FiltrosReportesUtil
									.convertAgeToDateOfBirth(edad2);
						}
						if (operador.equals(FiltrosReportesUtil.ENTRE)) {
							// se invierte el orden de las fechas.
							query.append("beneficiario."
									+ FiltrosReportesUtil.FECHA_NACIMIENTO
									+ " " + FiltrosReportesUtil.ENTRE
									+ " TO_DATE(:fechaFin_" + i
									+ ", 'dd/mm/rrrr') AND TO_DATE(:fechaIni_"
									+ i + ", 'dd/mm/rrrr')");
							sbQuery.append("beneficiario_."
									+ FiltrosReportesUtil.FECHA_NACIMIENTO
									+ " " + FiltrosReportesUtil.ENTRE
									+ " TO_DATE(:fechaFin_" + i
									+ ", 'dd/mm/rrrr') AND TO_DATE(:fechaIni_"
									+ i + ", 'dd/mm/rrrr')");
							namedParameters.put("fechaFin_" + i, fechaFin);
							namedParameters.put("fechaIni_" + i, fechaIni);

						} else {// se invierten los operadores debido a que
								// ahora se comparan fechas y no edades.
							if (operador.equals(FiltrosReportesUtil.MAYOR)) {
								operador = FiltrosReportesUtil.MENOR;
							} else if (operador
									.equals(FiltrosReportesUtil.MAYOR_IGUAL)) {
								operador = FiltrosReportesUtil.MENOR_IGUAL;
							} else if (operador
									.equals(FiltrosReportesUtil.MENOR)) {
								operador = FiltrosReportesUtil.MAYOR;
							} else if (operador
									.equals(FiltrosReportesUtil.MENOR_IGUAL)) {
								operador = FiltrosReportesUtil.MAYOR_IGUAL;
							}
							query.append("beneficiario."
									+ FiltrosReportesUtil.FECHA_NACIMIENTO
									+ " " + operador + " TO_DATE(:fechaIni_"
									+ i + ", 'dd/mm/rrrr')");
							sbQuery.append("beneficiario_."
									+ FiltrosReportesUtil.FECHA_NACIMIENTO
									+ " " + operador + " TO_DATE(:fechaIni_"
									+ i + ", 'dd/mm/rrrr')");
							namedParameters.put("fechaIni_" + i, fechaIni);
						}
						continue;
					}

					if (operador.equals(FiltrosReportesUtil.IGUAL)
							|| operador.equals(FiltrosReportesUtil.DIFERENTE)
							|| operador.equals(FiltrosReportesUtil.MENOR_IGUAL)
							|| operador.equals(FiltrosReportesUtil.MAYOR_IGUAL)
							|| operador.equals(FiltrosReportesUtil.MENOR)
							|| operador.equals(FiltrosReportesUtil.MAYOR)) {
						query.append(" beneficiario." + filtro.getCodcampo()
								+ " " + operador + " :filtro.valor_" + i + " ");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						sbQuery.append(" beneficiario_." + filtro.getCodcampo()
								+ " " + operador + " :filtro.valor_" + i + " ");

					} else {// BETWEEN
						query.append("beneficiario." + filtro.getCodcampo()
								+ " " + operador + " :filtro.valor_" + i
								+ " AND :filtro.valor2_" + i + " ");
						sbQuery.append("beneficiario_." + filtro.getCodcampo()
								+ " " + operador + " :filtro.valor_" + i
								+ " AND :filtro.valor2_" + i + " ");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						namedParameters.put("filtro.valor2_" + i,
								filtro.getValor2());
					}

				} else if (tipoDato.equals(FiltrosReportesUtil.TIPO_FECHA)) {

					if (operador.equals(FiltrosReportesUtil.ENTRE)
							|| operador.equals(FiltrosReportesUtil.IGUAL)) {
						query.append("beneficiario." + filtro.getCodcampo()
								+ " " + operador + " TO_DATE(:filtro.valor_"
								+ i
								+ ", 'dd/mm/rrrr') AND TO_DATE(:filtro.valor2_"
								+ i + ",'dd/mm/rrrr')");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						namedParameters.put("filtro.valor2_" + i,
								filtro.getValor2());
						sbQuery.append("beneficiario_." + filtro.getCodcampo()
								+ " " + operador + " TO_DATE(:filtro.valor_"
								+ i
								+ ", 'dd/mm/rrrr') AND TO_DATE(:filtro.valor2_"
								+ i + ", 'dd/mm/rrrr')");

					} else {
						query.append("beneficiario." + filtro.getCodcampo()
								+ " " + operador + " TO_DATE(:filtro.valor_"
								+ i + ", 'dd/mm/rrrr')");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						sbQuery.append("beneficiario_." + filtro.getCodcampo()
								+ " " + operador + " TO_DATE(:filtro.valor_"
								+ i + ", 'dd/mm/rrrr')");

					}

				} else if (tipoDato
						.equals(FiltrosReportesUtil.TIPO_ALFANUMERICO)
						|| tipoDato.equals("L")) {
					if (operador.equals(FiltrosReportesUtil.IGUAL)
							|| operador.equals(FiltrosReportesUtil.DIFERENTE)) {
						query.append(" UPPER(beneficiario."
								+ filtro.getCodcampo() + " ) " + operador
								+ " :filtro.valor_" + i + " ");
						namedParameters.put("filtro.valor_" + i, filtro
								.getValor().toUpperCase());
						sbQuery.append(" UPPER(beneficiario_."
								+ filtro.getCodcampo() + " ) " + operador
								+ " :filtro.valor_" + i + " ");

					} else if (operador.equals(FiltrosReportesUtil.PARECIDO)) {
						query.append(" UPPER(beneficiario."
								+ filtro.getCodcampo() + " ) "
								+ FiltrosReportesUtil.PARECIDO
								+ " :filtro.valor_" + i + " ");
						namedParameters.put("filtro.valor_" + i, "%"
								+ filtro.getValor().toUpperCase() + "%");
						sbQuery.append(" UPPER(beneficiario_."
								+ filtro.getCodcampo() + " ) "
								+ FiltrosReportesUtil.PARECIDO
								+ " :filtro.valor_" + i + " ");

					}
				} else {
					if (operador.equals(FiltrosReportesUtil.IGUAL)
							|| operador.equals(FiltrosReportesUtil.DIFERENTE)
							|| operador.equals(FiltrosReportesUtil.MENOR_IGUAL)
							|| operador.equals(FiltrosReportesUtil.MAYOR_IGUAL)
							|| operador.equals(FiltrosReportesUtil.MENOR)
							|| operador.equals(FiltrosReportesUtil.MAYOR)) {
						query.append(" beneficiario." + filtro.getCodcampo()
								+ " " + operador + " :filtro.valor_" + i + " ");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						query.append(" beneficiario_." + filtro.getCodcampo()
								+ " " + operador + " :filtro.valor_" + i + " ");

					} else { // BETWEEN
						query.append("beneficiario." + filtro.getCodcampo()
								+ " " + operador + " :filtro.valor_" + i
								+ " AND :filtro.valor2_" + i + " ");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						namedParameters.put("filtro.valor2_" + i,
								filtro.getValor2());
						sbQuery.append("beneficiario_." + filtro.getCodcampo()
								+ " " + operador + " :filtro.valor_" + i
								+ " AND :filtro.valor2_" + i + " ");

					}

				}

			}
		}
		ret[0] = query.toString();
		ret[1] = namedParameters;
		ret[2] = sbQuery.toString();
		// ret[3] = sbQueryParams;
		// System.out.println("retorno: " + retorno);
		return ret;
	}
}
